<?php

namespace Qm;

class ContentParser {

    private $oContent   = NULL;
    private $oQuery     = NULL;
    private $oFamily    = NULL;
    private $aCode      = NULL;
    private $bOpenTag   = FALSE;
    private $iOpenIf    = 0;
    private $iOpenElse  = 0;
    private $iLine      = 0;

    private $sToEval = '';

    public function setFileContent( \Qm\FileContent $oFileContent ) {
        $this->oContent = $oFileContent;
    }

    public function parseFileContent() {

        if( is_null( $this->oContent ) ) {
            throw new LogicException( 'File content object has not been set yet.' );
        } #if

        foreach( $this->oContent->aFamily as $oFamily ) {

            $this->oFamily = $oFamily;

            foreach( $oFamily->aQuery as $oQuery ) {

                $this->oQuery = $oQuery;

                foreach( $oQuery->aLine as $cConstParsing => $sLine ) {

                    $this->iLine = $sLine;
                    $this->buildLine( $sLine );

                }
/*
                $this->sToEval =
                        '$ahoj=array( "krava" => false, 50 ); $a = 10; $b = 3;$sBuildedQuery = NULL;' ."\n".
                        $this->sToEval.
                        'return $sBuildedQuery;';
                echo $this->sToEval ."\n-------------------------------\n";
                echo eval($this->sToEval)."\n-------------------------------\n";
*/
            }
        }
    }

    private function buildLine( $sLine ) {

        $sAtomPattern = '/(\{[^\}]*\})/';
        $aAtom = preg_split( $sAtomPattern, $sLine , -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );

        foreach( $aAtom as $sAtom ) {
            if( $sAtom[0] == '{' ) {
                // strip curly brackets
                $sTag = trim ( $sAtom, '{}' );

                // translate variables and constants into PHP
                $sTag = $this->replaceVarsAndConsts( $sTag );
                var_dump( $sTag );
            }
        }


        /*
        $sTagPattern = '/
                (\{(?:(?<primitive>
                        null |
                        false |
                        true |
                        (?:[+-]?0x[0-9a-fA-F]+) |
                        [+-]?\d+(?:\.\d+)? |
                        "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" |
                        \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'))\}) |
                (\{(?<var>\$[\w\.\d\$]+)([\+\-\*\/\%](?&var) | (?&primitive))*\}) |
                (\{loop(?: name){0,1}="\${0,1}[^"]*"\}) |
                (\{\/loop\}) |
                (\{if(?: condition){0,1}="[^"]*"\}) |
                (\{elseif(?: condition){0,1}="[^"]*"\}) |
                (\{else\}) |
                (\{\/if\}) |
                (\{function="[^"]*"\}) |
                (\{noparse\}) |
                (\{\/noparse\}) |
                (\{ignore\}|\{\*) |
                (\{\/ignore\}|\*\}) |
                (\{function="(\w*?)(?:.*?)"\})
            /iSx';

        $aAtom = preg_split ( $sTagPattern, $sLine , -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
        $sCompiledLine = '$sBuildedQuery .= "\n";'."\n";

        echo "\natoms:\n";
        var_dump( $aAtom );
        echo "\n\n";

        foreach( $aAtom as $sAtom ) {

            if( preg_match( '/\{(?<var>\$[\w\.\d\$]+)\}/', $sAtom, $aCode ) ) {
                $sCompiledLine .= "\$sBuildedQuery .= {$this->substituteVariable( $this->buildVariable( $aCode['var'] ) )};\n";

            } elseif( preg_match( '/\{if="(?<condition>[^"]*)"\}/', $sAtom, $aCode ) ) {
                ++$this->iOpenIf;
                $sCompiledLine .=   "if( {$aCode['condition']} ){\n";

            } elseif( preg_match( '/\{elseif="(?<condition>[^"]*)"\}/', $sAtom, $aCode ) ) {
                if( 0 == $this->iOpenIf ) {
                    throw new SyntaxException(
                        'Unexcepted {elseif} tag.',
                        $this->oFamily->sSourceFile,
                        $this->iLine );
                }
                $sCompiledLine .=   "}elseif( {$aCode['condition']} ){\n";

            } elseif( strpos( $sAtom, '{else}' ) !== FALSE ) {
                if( 0 == $this->iOpenIf ) {
                    throw new SyntaxException(
                        'Unexcepted {else} tag.',
                        $this->oFamily->sSourceFile,
                        $this->iLine );
                }

                if( $this->iOpenIf < ++$this->iOpenElse ) {
                    throw new SyntaxException(
                        'Only one {else} tag is allowed in if-block.',
                        $this->oFamily->sSourceFile,
                        $this->iLine );
                }
                $sCompiledLine .=   "}else{\n";

            } elseif( strpos( $sAtom, '{/if}' ) !== FALSE ) {
                if( 0 > --$this->iOpenIf ) {
                    throw new SyntaxException(
                        'Unexcepted if-block closure (tag {/if}).',
                        $this->oFamily->sSourceFile,
                        $this->iLine );
                }
                $this->iOpenElse -= $this->iOpenElse ? 1 : 0;
                $sCompiledLine .=   "}\n";
            } else {
                $sCompiledLine .=   "\$sBuildedQuery .= '$sAtom';\n";
            }
        }

        $this->sToEval .= $sCompiledLine;

         */

    }

    private function substituteVariable( $sVariable ) {

        return 'is_array('.$sVariable.')?empty('.$sVariable.')?\'(NULL)\':(\'(\'.implode( \',\',array_map(\'Qm\varValueToString\', '.$sVariable.')).\')\'):Qm\varValueToString('.$sVariable.')';

    }

    function buildVariable( $sVariable ) {

        if( preg_match( '/^\$\w+$/', $sVariable ) ) {
            return $sVariable;
        } #if

        if( preg_match( '/^\$\w+(?:\.(?:\w+|\d+))+$/', $sVariable ) ) {
            $aVariable = explode( '.', $sVariable );
            $sCompiledVariable = array_shift( $aVariable );
            foreach( $aVariable as $sKey ) {
                if( is_numeric( $sKey ) ) {
                    $sCompiledVariable .= '['.$sKey.']';
                } else {
                    $sCompiledVariable .= '[\''. str_replace( '\'', '\\\'', $sKey ) .'\']';
                }
            }
            return $sCompiledVariable;
        } #if

        throw new SyntaxException(
            "Invalid syntax for variable '{$sVariable}'.",
            $this->oFamily->sSourceFile,
            $this->iLine );

    }

    function replaceVarsAndConsts( $sString ) {

        $cArrayParsing = function( $aMatch ) {

            $sArray = $aMatch['name'];

            $sKeys  = substr( $aMatch[0], strlen( $aMatch['name'] ) + 1 );
            $aKey   = explode( '.', $sKeys );

            foreach( $aKey as $sKey ) {
                if( is_numeric( $sKey ) ) {
                    $sArray .= '['.$sKey.']';
                } else {
                    $sArray .= '[\''. str_replace( '\'', '\\\'', $sKey ) .'\']';
                }
            }
            return $sArray;

        };

        $sArrayPattern = '/(?<name>\$\w+)(?<key>\.(?:\w+|\d+))+/';
        $sString = preg_replace_callback( $sArrayPattern, $cArrayParsing, $sString );

        $cConstParsing = function( $aMatch ) {

            switch( $aMatch['name'] ) {
                case 'lCurlyBracket' :
                    return "'{'";
                case 'rCurlyBracket' :
                    return "'{'";
                default:
                    return $aMatch['name'];
            }

        };

        $sConstPattern = '/\#(?<name>\w+)/';
        $sString = preg_replace_callback( $sConstPattern, $cConstParsing, $sString );

        return $sString;

    }

}
